/*
 * todo : comments here 
 * 
 */

package threadlogging.common;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.NoSuchElementException;


/**
 * 
 * cyclic buffer
 * 
 */
public class CyclicBuffer<E>
{

	protected ArrayList<E> elements;
	protected int first;
	protected int last;
	protected int numElems;
	protected int maxSize;

	/**
	 * Instantiate a new CyclicBuffer of at most <code>maxSize</code> events.
	 * 
	 * The <code>maxSize</code> argument must a positive integer.
	 * 
	 * @param maxSize
	 *            The maximum number of elements in the buffer.
	 */
	public CyclicBuffer(int maxSize) throws IllegalArgumentException
	{
		if (maxSize < 1)
		{
			throw new IllegalArgumentException("The maxSize argument ("
					+ maxSize + ") is not a positive integer.");
		}
		this.maxSize = maxSize;
		elements = new ArrayList<E>(this.maxSize);
		first = 0;
		last = 0;
		numElems = 0;
	}

	/**
	 * Add an <code>event</code> as the last event in the buffer.
	 */
	public void add(E element)
	{
		if (numElems < maxSize)
		{
			elements.add(element);
		}
		else
		{
			elements.set(last, element);
		}

		if (++last == maxSize)
		{
			last = 0;
		}

		if (numElems < maxSize)
		{
			numElems++;
		}
		else if (++first == maxSize)
		{
			first = 0;
		}
	}

	/**
	 * Get the <i>i</i>th oldest event currently in the buffer. If <em>i</em> is
	 * outside the range 0 to the number of elements currently in the buffer,
	 * then <code>null</code> is returned.
	 */
	public E get(int i)
	{
		if (i < 0 || i >= numElems)
		{
			return null;
		}

		int position = (first + i) % maxSize;
		
		return elements.get(position);
		
	}

	public int getMaxSize()
	{
		return maxSize;
	}

	/**
	 * Get the oldest (first) element in the buffer. The oldest element is
	 * removed from the buffer.
	 */
	public E get()
	{
		E r = null;
		if (numElems > 0)
		{
			numElems--;
			r = elements.get(first);
			elements.set(first, null);
			if (++first == maxSize)
			{
				first = 0;
			}
		}
		return r;
	}

	/**
	 * Get the number of elements in the buffer. This number is guaranteed to be
	 * in the range 0 to <code>maxSize</code> (inclusive).
	 */
	public int length()
	{
		return numElems;
	}

	public void resize(int newSize)
	{
		// todo : implement
	} 


	public Iterator<E> iterator()
	{
		return new CyclicBufferIterator<E>(this, false);
	}

	public Iterator<E> reverseIterator()
	{
		return new CyclicBufferIterator<E>(this, true);
	}

	static class CyclicBufferIterator<E> implements Iterator<E>
	{
		private int next = -1;
		private CyclicBuffer<E> cb;
		private boolean reverse = false;

		public CyclicBufferIterator(CyclicBuffer<E> buffer,
				boolean reverseIteration)
		{
			cb = buffer;

			reverse = reverseIteration;

			if (buffer.length() == 0)
			{
				next = -1;
			}
			else if (reverse)
			{
				next = cb.length() - 1;
			}
			else
			{
				next = 0;
			}
		}

		public boolean hasNext()
		{
			if (cb.length() == 0)
			{
				return false;
			}
			else if (reverse)
			{
				return (next >= 0);
			}
			else
			{
				return (next < (cb.length()));
			}

		}

		public E next()
		{
			if ((cb.length() == 0) || (next == -1))
			{
				throw new NoSuchElementException();
			}
			E obj = cb.get(next);
			updateNext();
			return obj;
		}

		protected void updateNext()
		{
			if (reverse)
			{
				next--;
			}
			else
			{
				next++;
			}
		}

		public void remove()
		{
			throw new UnsupportedOperationException("remove() is unsupported");
		}
	}
}
